#pragma once

#include <iostream>
#include "lua.hpp"
#include "LuaBridge/LuaBridge.h"

namespace LuaGui {
	class ScriptApplication {
	public:
		static ScriptApplication* GetMainInstance() {
			static ScriptApplication s_script_application;
			return &s_script_application;
		}
	public:
		template <typename... T>
		void CallGlobalFunction(const char* funcname, T&&... args) {}

		virtual void DoString(const char* script_text) {}
		virtual void DoFile(const char* script_file) {}
		virtual void OnTimer() {}
		virtual void OnPaint() {}
		virtual void OnSize(int width, int height) {}
		virtual void OnMouseDown(int mouse_x, int mouse_y, int mouse_button = 0) {}
		virtual void OnMouseMove(int mouse_x, int mouse_y) {}
		virtual void OnMouseUp(int mouse_x, int mouse_y, int mouse_button = 0) {}
		virtual void OnMouseWheel(int mouse_x, int mouse_y) {}
		virtual void OnKeyDown(unsigned int key) {}
		virtual void OnKeyUp(unsigned int key) {}
		virtual void OnChar(const char* utf8) {}
	};

	class LuaScriptApplication : public ScriptApplication {
	public:
		static LuaScriptApplication* GetMainInstance() {
			static LuaScriptApplication s_lua_script_application;
			return &s_lua_script_application;
		}

	public:
		template <typename... T>
		void CallGlobalFunction(const char* funcname, T&&... args) {
			luabridge::LuaRef func = luabridge::getGlobal(m_lua_state, funcname);
			if (!func.isFunction()) return;
			try {
				func(std::forward<T>(args)...);
			}
			catch (luabridge::LuaException const& e) {
				std::cerr << e.what() << std::endl;
				//GetApplication()->Warn(e.what());
			}
		}

		LuaScriptApplication() {
			m_lua_state = luaL_newstate();
			luaL_openlibs(m_lua_state);
		}

		~LuaScriptApplication() {
			lua_close(m_lua_state);
		}

		lua_State* GetLuaState() { return m_lua_state; }

		virtual void DoString(const char* script_text) {
			int result = luaL_dostring(m_lua_state, script_text);
			if (result != 0)
			{
				//GetApplication()->Warn(lua_tostring(m_lua_state, -1));
			}
		}

		virtual void DoFile(const char* script_file) {
			int result = luaL_dofile(m_lua_state, script_file);
			if (result != 0)
			{
				//GetApplication()->Warn(lua_tostring(m_lua_state, -1));
			}
		}

		virtual void OnTimer() {
			CallGlobalFunction("__on_timer__");
		}
		virtual void OnPaint() {
			CallGlobalFunction("__on_paint__");
		}
		virtual void OnSize(int width, int height) {
			CallGlobalFunction("__on_size__", width, height);
		}
		virtual void OnMouseDown(int mouse_x, int mouse_y, int mouse_button = 0) {
			CallGlobalFunction("__on_mouse_down__", mouse_x, mouse_y, mouse_button);
		}
		virtual void OnMouseMove(int mouse_x, int mouse_y) {
			CallGlobalFunction("__on_mouse_move__", mouse_x, mouse_y);
		}
		virtual void OnMouseUp(int mouse_x, int mouse_y, int mouse_button = 0) {
			CallGlobalFunction("__on_mouse_up__", mouse_x, mouse_y, mouse_button);
		}
		virtual void OnMouseWheel(int mouse_x, int mouse_y) {
			CallGlobalFunction("__on_mouse_wheel__", mouse_x, mouse_y);
		}
		virtual void OnKeyDown(unsigned int key) {
			CallGlobalFunction("__on_key_down__", key);
		}
		virtual void OnKeyUp(unsigned int key) {
			CallGlobalFunction("__on_key_up__", key);
		}
		virtual void OnChar(const char* utf8) {
			CallGlobalFunction("__on_char__", utf8);
		}
	protected:
		lua_State* m_lua_state;
	};
}

namespace LuaGui {
	template <typename... T>
	void CallLuaGlobalFunction(const char* funcname, T&&... args) {
		LuaScriptApplication::GetMainInstance()->CallGlobalFunction(funcname, std::forward<T>(args)...);
	}
}